/**
 * MultiClassLoader.java
 * 
 * FreeZzaph is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * FreeZzaph is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file COPYING.
 */
package freezzaph.classloader;

import java.util.Hashtable;

/**
 * A simple test class loader capable of loading from
 * multiple sources, such as local files or a URL.
 *
 * @author Jack Harich - 8/18/97
 * @author John D. Mitchell - 99.03.04
 * @author FreeZzaph
 */
public abstract class MultiClassLoader extends ClassLoader {

	private Hashtable<String, Class<?>> classes =
			new Hashtable<String, Class<?>>();
	private char classNameReplacementChar;

	public MultiClassLoader() {
		// Does nothing, woo!
	}

	/**
	 * This is a simple version for external clients since they
	 * will always want the class resolved before it is returned
	 * to them.
	 */
	@Override public Class<?> loadClass(String className)
			throws ClassNotFoundException {
		return (loadClass(className, true));
	}

	@Override public synchronized Class<?> loadClass(String className,
			boolean resolveIt) throws ClassNotFoundException {

		Class<?> result;
		byte[] classBytes;

		// Check our local cache of classes
		result = classes.get(className);
		if (result != null) {
			return result;
		}

		// Check with the primordial class loader
		try {
			result = super.findSystemClass(className);
			return result;
		} catch (ClassNotFoundException e) { /**/
		}

		// Try to load it from preferred source
		// NOTE: loadClassBytes() is an abstract method
		classBytes = loadClassBytes(className);
		if (classBytes == null) {
			throw new ClassNotFoundException();
		}

		// Define it (parse the class file)
		result = defineClass(className, classBytes, 0, classBytes.length);
		if (result == null) {
			throw new ClassFormatError();
		}

		// Resolve if necessary
		if (resolveIt)
			resolveClass(result);

		// Done
		classes.put(className, result);
		return result;
	}

	protected abstract byte[] loadClassBytes(String className);

	protected String formatClassName(String className) {
		if (classNameReplacementChar == '\u0000') {
			// '/' is used to map the package to the path
			return className.replace('.', '/') + ".class";
		}
		// Replace '.' with custom char, such as '_'
		return className.replace('.', classNameReplacementChar) + ".class";
	}

}
